<?php

namespace app\command;

use RuntimeException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use think\facade\Db;

class Generate extends Command
{
    protected static $defaultName = 'Generate';
    protected static $defaultDescription = 'Generate';

    /**
     * 跳过的字段
     */
    private const SKIP_FIELDS = ['id', 'created_at', 'updated_at','deleted_at', 'is_del', 'status'];

    /**
     * 文件类型映射
     */
    private const FILE_TYPES = [
        1 => ['name' => 'model', 'suffix' => 'Model.php'],
        2 => ['name' => 'dao', 'suffix' => 'Dao.php'],
        3 => ['name' => 'repositories', 'suffix' => 'Repository.php']
    ];

    /**
     * 控制器类型映射
     */
    private const CONTROLLER_TYPES = [
        7 => 'admin',
        8 => 'api'
    ];

    private const DEFAULT_VALUES = [
        'int' => '0',
        'string' => '""',
        'float' => '0.0',
        'boolean' => 'false',
        'datetime' => 'null',
        'default' => 'null'
    ];

    /**
     * 解析字段注释
     */
    protected function parseComment(string $comment = ''): string
    {
        if (empty($comment)) {
            return '';
        }

        $comment = str_replace('，', ',', $comment);
        $separators = [',', ' '];

        foreach ($separators as $separator) {
            if (strpos($comment, $separator) !== false) {
                return explode($separator, $comment)[0];
            }
        }

        return $comment;
    }

    /**
     * 构建搜索规则
     */
    protected function buildOneSearchRule(string $field, string $type): string
    {
        $type = preg_quote($type);
        $searchRules = [];

        // 整数类型搜索
        if (preg_match("/(?:int|tinyint|bigint)/", $type)) {
            $searchRules[] = $this->buildIntegerSearchRule($field);
        }

        // 字符串类型搜索
        if (preg_match("/varchar/", $type)) {
            $searchRules[] = $this->buildVarcharSearchRule($field);
            $searchRules[] = $this->buildExactSearchRule($field);
        }

        $searchString = implode('', $searchRules);
        return str_replace('"', "'", $searchString);
    }

    /**
     * 构建整数搜索规则
     */
    private function buildIntegerSearchRule(string $field): string
    {
        return sprintf(
            "->when(isset(\$where['%s']) && \$where['%s'] !== '', function (\$query) use (\$where) {
                \$query->where('%s', \$where['%s']);
            })",
            $field, $field, $field, $field
        );
    }

    /**
     * 构建模糊搜索规则
     */
    private function buildVarcharSearchRule(string $field): string
    {
        return sprintf(
            "->when(isset(\$where['%s']) && \$where['%s'] !== '', function (\$query) use (\$where) {
                \$query->whereLike('%s', '%%' . trim(\$where['%s']) . '%%');
            })",
            $field, $field, $field, $field
        );
    }

    /**
     * 构建精确搜索规则
     */
    private function buildExactSearchRule(string $field): string
    {
        return sprintf(
            "->when(isset(\$where['%s_key']) && \$where['%s_key'] !== '', function (\$query) use (\$where) {
                \$query->where('%s', \$where['%s_key']);
            })",
            $field, $field, $field, $field
        );
    }

    /**
     * 获取表字段信息
     */
    private function getTableColumns(string $tableName): array
    {
        $config = Db::getConfig();
        $prefix = $config['connections'][$config['default']]['prefix'] ?? '';
        return Db::query('SHOW FULL COLUMNS FROM ' . $prefix . $tableName);
    }


    /**
     * 检查文件是否存在
     */
    protected function checkFileExists(string $path, OutputInterface $output): bool
    {
        if (file_exists($path)) {
            $output->writeln("<error>File already exists:</error> {$path}");
            return true;
        }
        return false;
    }



    protected function configure()
    {
        $this
            ->setName('Generate')
            ->setDescription('Generate Model/Dao/Repository/Controller')
            ->addArgument('table', InputArgument::REQUIRED, 'Table name')
            ->addArgument('module', InputArgument::REQUIRED, 'Module name');
    }

    /**
     * 执行命令
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        try {
            $table = $input->getArgument('table');
            $module = $input->getArgument('module');

            if (!$this->handleExistingFiles($module, $table, $output)) {
                return 0;
            }

            $this->generateFiles($module, $table, $output);
            return 0;
        } catch (\Exception $e) {
            $output->writeln("<error>Error: {$e->getMessage()}</error>");
            return 1;
        }
    }

    /**
     * 处理已存在的文件
     */
    private function handleExistingFiles(string $module, string $table, OutputInterface $output): bool
    {
        $entityName = $this->getEntityName($table);
        $existingFiles = [];

        // 检查基础文件
        foreach (self::FILE_TYPES as $type => $info) {
            $path = $this->buildFilePath((string)$type, $module, $entityName);
            if (file_exists($path)) {
                $existingFiles[] = $path;
            }
        }

        if (!empty($existingFiles)) {
            foreach ($existingFiles as $file) {
                $output->writeln("<error>File already exists:</error> {$file}");
            }
            return strtolower(readline('Files already exist, overwrite? (y/n): ')) === 'y';
        }

        return true;
    }

    /**
     * 生成所有文件
     */
    private function generateFiles(string $module, string $table, OutputInterface $output): void
    {
        // 生成基础文件
        $this->generateBaseFiles($module, $table, $output);
        
        // 生成控制器文件
        $this->generateControllerFiles($module, $table, $output);
    }

    /**
     * 生成基础文件
     */
    private function generateBaseFiles(string $module, string $table, OutputInterface $output): void
    {
        $entityName = $this->getEntityName($table);
        $arguments = $this->buildCommonArguments($table);

        foreach (self::FILE_TYPES as $type => $info) {
            $this->generateFile($type, $module, $table, $entityName, $arguments, $output);
        }
    }

    /**
     * 生成控制器文件
     */
    private function generateControllerFiles(string $module, string $table, OutputInterface $output): void
    {
        foreach (self::CONTROLLER_TYPES as $type => $name) {
            if (strtolower(readline("Generate {$name} Controller (y/n): ")) === 'y') {
                $this->generateControllerFile($type, $module, $table, $output);
            }
        }
    }

    /**
     * 生成单个文件
     */
    private function generateFile(string $type, string $module, string $table, string $entityName, array $arguments, OutputInterface $output): void
    {
        $stubName = self::FILE_TYPES[$type]['name'];
        $content = $this->generateFileContent($stubName, [
            '{{module}}' => $module,
            '{{table}}' => $table,
            '{{EntityName}}' => $entityName,
            '{{inputArguments}}' => $arguments[0] ?? '',
            '{{callArguments}}' => $arguments[1] ?? '',
            '{{functionArguments}}' => $arguments[2] ?? '',
            '{{descArguments}}' => $arguments[3] ?? '',
            '{{joinArguments}}' => $arguments[4] ?? '',
            '{{seachArguments}}' => $arguments[5] ?? ''
        ]);

        $path = $this->buildFilePath($type, $module, $entityName);
        $this->writeFile($path, $content, ucfirst($stubName), $output);
    }

    /**
     * 生成控制器文件
     */
    private function generateControllerFile(int $type, string $module, string $table, OutputInterface $output): void
    {
        $entityName = $this->getEntityName($table);
        $arguments = $this->buildCommonArguments($table);
        $controllerType = self::CONTROLLER_TYPES[$type];

        $content = $this->generateFileContent($controllerType . 'controller', [
            '{{EntityName}}' => $entityName,
            '{{module}}' => $module,
            '{{inputArguments}}' => $arguments[0] ?? '',
            '{{callArguments}}' => $arguments[1] ?? '',
            '{{functionArguments}}' => $arguments[2] ?? '',
            '{{descArguments}}' => $arguments[3] ?? '',
            '{{joinArguments}}' => $arguments[4] ?? '',
            '{{seachArguments}}' => $arguments[5] ?? ''
        ]);

        $path = $this->buildFilePath((string)$type, $module, $entityName);
        $this->writeFile($path, $content, ucfirst($controllerType) . ' Controller', $output);
    }

    /**
     * 生成文件内容
     */
    private function generateFileContent(string $stubName, array $replacements): string
    {
        $stubPath = __DIR__ . "/stubs/{$stubName}.stub";
        if (!file_exists($stubPath)) {
            throw new RuntimeException("Stub file not found: {$stubPath}");
        }

        $stub = file_get_contents($stubPath);
        if ($stub === false) {
            throw new RuntimeException("Failed to read stub file: {$stubPath}");
        }

        return str_replace(array_keys($replacements), array_values($replacements), $stub);
    }

    /**
     * 构建通用参数
     */
    private function buildCommonArguments(string $tableName): array
    {
        $columns = $this->getTableColumns($tableName);
        $validColumns = $this->filterValidColumns($columns);
        $maxLengths = $this->calculateMaxLengths($validColumns);
        
        $arguments = [
            'InputInterface' => [],
            'call' => [],
            'function' => [],
            'desc' => [],
            'join' => [],
            'search' => []
        ];

        foreach ($validColumns as $column) {
            $this->processColumn($column, $arguments, $maxLengths);
        }

        return $this->formatArguments($arguments);
    }

    /**
     * 处理单个字段
     */
    private function processColumn(array $column, array &$arguments, array $maxLengths): void
    {
        $field = $column['Field'];
        $type = $column['Type'];
        $baseType = $this->getBaseType($type);
        $default = $this->getDefaultValue($column);
        
        $arguments['InputInterface'][] = "\${$field} = request()->post('{$field}',{$default});";
        $arguments['call'][] = "\${$field},";
        $arguments['function'][] = "\${$field} = {$default},";
        $arguments['desc'][] = $this->buildDocComment($baseType, $field, $column['Comment'], $maxLengths);
        $arguments['join'][] = "'{$field}'";
        
        $searchRule = $this->buildOneSearchRule($field, $type);
        if (!empty($searchRule)) {
            $arguments['search'][] = $searchRule;
        }
    }

    /**
     * 构建文档注释
     */
    private function buildDocComment(string $type, string $field, string $comment, array $maxLengths): string
    {
        $typePadding = str_repeat(' ', $maxLengths['type'] - strlen($type));
        $fieldPadding = str_repeat(' ', $maxLengths['field'] - strlen($field));
        
        return sprintf(
            " * @param %s%s $%s%s %s",
            $type,
            $typePadding,
            $field,
            $fieldPadding,
            $this->parseComment($comment)
        );
    }

    /**
     * 过滤有效字段
     */
    private function filterValidColumns(array $columns): array
    {
        return array_filter($columns, function($column) {
            return !in_array($column['Field'], self::SKIP_FIELDS);
        });
    }

    /**
     * 获取字段基本类型
     */
    private function getBaseType(string $type): string
    {
        return preg_replace('/\(.*\)/', '', $type);
    }

    /**
     * 获取默认值
     */
    private function getDefaultValue(array $column): string
    {
        $default = $column['Default'] ?? null;
        if ($default !== null) {
            return $default === '' ? "''" : $default;
        }

        $baseType = $this->getBaseType($column['Type']);
        return self::DEFAULT_VALUES[$baseType] ?? self::DEFAULT_VALUES['default'];
    }

    /**
     * 计算最大长度
     */
    private function calculateMaxLengths(array $columns): array
    {
        $maxTypeLength = 0;
        $maxFieldLength = 0;

        foreach ($columns as $column) {
            $baseType = $this->getBaseType($column['Type']);
            $maxTypeLength = max($maxTypeLength, strlen($baseType));
            $maxFieldLength = max($maxFieldLength, strlen($column['Field']));
        }

        return [
            'type' => $maxTypeLength,
            'field' => $maxFieldLength
        ];
    }

    /**
     * 格式化最终参数
     */
    private function formatArguments(array $arguments): array
    {
        return [
            implode(PHP_EOL, $arguments['InputInterface']),
            implode(PHP_EOL, $arguments['call']),
            implode(PHP_EOL, $arguments['function']),
            implode(PHP_EOL, $arguments['desc']),
            implode(',', $arguments['join']),
            implode('', $arguments['search'])
        ];
    }

    /**
     * 获取实体名称
     */
    private function getEntityName(string $table): string
    {
        return implode('', array_map('ucfirst', explode('_', $table)));
    }

    /**
     * 构建文件路径
     */
    protected function buildFilePath(string $type, string $module, string $entityName): string
    {
        if (isset(self::FILE_TYPES[$type])) {
            $fileType = self::FILE_TYPES[$type];
            return app_path() . "/common/{$fileType['name']}/{$module}/{$entityName}{$fileType['suffix']}";
        }

        if (isset(self::CONTROLLER_TYPES[$type])) {
            $controllerType = self::CONTROLLER_TYPES[$type];
            return app_path() . "/{$controllerType}/controller/{$entityName}Controller.php";
        }

        throw new RuntimeException("Invalid file type: {$type}");
    }

    /**
     * 写入文件
     */
    protected function writeFile(string $path, string $content, string $type, OutputInterface $output): void
    {
        $dir = dirname($path);
        if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) {
            throw new RuntimeException("Failed to create directory: {$dir}");
        }

        if (file_put_contents($path, $content) === false) {
            throw new RuntimeException("Failed to write {$type} file: {$path}");
        }

        $output->writeln("<info>{$type} created:</info> {$path}");
    }

}
